iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
Build on Google AI

從 Vibe Coding 到 Production:用 Google AI 打造上線守門員系列 第 7

Day 7|認證不等於授權:從登入功能找出越權風險

  • 分享至 

  • xImage
  •  

Day 6,Gemini 指出 Firestore Rules 可能允許跨帳號存取。

今天不再只看模型的文字判斷。我們會建立三種身分,直接向 Firestore Emulator 讀寫同一筆資料:

  1. 資料擁有者。
  2. 另一位已登入的使用者。
  3. 尚未登入的訪客。

我們要回答一個具體問題:系統知道使用者是誰之後,是否也確認他有權操作這筆資料?

Authentication 與 Authorization 差在哪裡?

Authentication 是認證。系統透過密碼、Passkey 或身分提供者,確認使用者是誰。

Authorization 是授權。系統根據資源擁有者、角色或權限,決定使用者可以執行哪些操作。

假設公司替每位員工發識別證。警衛看過識別證後,確認訪客是公司員工。這是認證。

員工刷卡進入機房時,門禁系統還要確認他是否具備機房權限。這是授權。

登入成功只能通過第一關。它不能自動證明使用者可以讀取所有資料。

目前的 Firestore Rules

Day 5 把規則改成以下內容:

match /projects/{projectId} {
  allow read, write: if request.auth != null;
}

request.auth != null 只確認請求來自已登入使用者。規則沒有比較登入者 UID 與文件中的 ownerId

這代表:

  • 未登入訪客不能讀取資料。
  • 任何已登入使用者都能讀寫 projects 中的文件。

第二項就是 Broken Access Control。系統完成認證,卻沒有執行資源層級的授權。

建立可重現的測試

專案已加入以下測試腳本:

demo-app/tests/authorization-scenario.js

腳本會先建立一筆私人專案:

{
  name: "Owner launch plan",
  launchGoal: "Only the owner should read this document",
  ownerId: "owner-user"
}

接著建立三個 Firestore Context:

const ownerDb = testEnvironment
  .authenticatedContext("owner-user")
  .firestore();

const attackerDb = testEnvironment
  .authenticatedContext("attacker-user")
  .firestore();

const guestDb = testEnvironment
  .unauthenticatedContext()
  .firestore();

這裡的 attacker-user 不具備管理員權限。他只是另一位正常登入的使用者。

腳本會執行四項操作:

  1. 擁有者讀取自己的專案。
  2. 未登入訪客讀取擁有者的專案。
  3. 另一位登入者讀取擁有者的專案。
  4. 另一位登入者修改擁有者的專案。

執行不安全規則

進入 Demo 專案:

cd /media/mickey/777/ithome/demo-app

執行授權情境:

npm run authz:demo

實際結果如下:

Owner reads own project            ALLOWED  expected=ALLOWED
Guest reads owner's project        DENIED   expected=DENIED
Other user reads owner's project   ALLOWED  expected=DENIED
Other user edits owner's project   ALLOWED  expected=DENIED

訪客遭到拒絕,代表 Authentication 檢查確實生效。

另一位登入者卻能讀取與修改資料。這證明規則沒有檢查 Authorization。

這次測試使用已知的 Document ID 讀取文件。實際攻擊者也可能直接查詢集合,因為 Firestore 的 read 權限包含單筆讀取與列表查詢。前端畫面沒有顯示資料,不代表後端拒絕存取。攻擊者可以繞過畫面,直接使用 SDK 或 REST API 發出請求。

這項漏洞會造成什麼影響?

攻擊者只需要建立一般帳號。他不需要取得管理員角色,也不需要竊取其他人的密碼。

成功利用後,攻擊者可能:

  • 讀取其他使用者的專案名稱與上線計畫。
  • 修改專案內容,讓擁有者看到錯誤資料。
  • 刪除其他使用者的文件。
  • 透過集合查詢取得大量資料。

嚴重度仍要根據資料內容判斷。如果文件只包含公開名稱,影響較低。如果文件包含個資、商業計畫或部署資訊,影響就會提高。

我們不能只根據規則寫法判定最終嚴重度。報告還要說明系統保存哪些資料,以及攻擊者能取得多少文件。

修正資源擁有者檢查

建立文件時,規則要檢查新資料中的 ownerId

allow create: if request.auth != null
  && request.resource.data.ownerId == request.auth.uid;

讀取與刪除文件時,規則要檢查既有資料的擁有者:

allow read, delete: if request.auth != null
  && resource.data.ownerId == request.auth.uid;

更新文件時,規則還要禁止使用者改寫 ownerId

allow update: if request.auth != null
  && resource.data.ownerId == request.auth.uid
  && request.resource.data.ownerId == resource.data.ownerId;

完整規則如下:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /projects/{projectId} {
      allow create: if request.auth != null
        && request.resource.data.ownerId == request.auth.uid;

      allow read, delete: if request.auth != null
        && resource.data.ownerId == request.auth.uid;

      allow update: if request.auth != null
        && resource.data.ownerId == request.auth.uid
        && request.resource.data.ownerId == resource.data.ownerId;
    }
  }
}

Firestore Rules 使用兩種資料狀態:

  • resource.data 是資料庫目前保存的文件。
  • request.resource.data 是寫入完成後的新文件。

更新時只檢查舊資料不夠。否則擁有者可能把 ownerId 改成其他值,破壞資料的權限模型。

再次執行安全版本

專案把修正版規則保存在:

review-target/secure-firestore.rules

執行:

npm run authz:secure

實際結果如下:

Owner reads own project            ALLOWED  expected=ALLOWED
Guest reads owner's project        DENIED   expected=DENIED
Other user reads owner's project   DENIED   expected=DENIED
Other user edits owner's project   DENIED   expected=DENIED

修正後,擁有者仍能操作自己的資料。訪客與其他登入者則遭到拒絕。

這項結果比「規則看起來比較安全」更有說服力。它證明四個指定情境都符合預期。

這項測試如何幫助 Gemini Reviewer?

Day 6 的 Gemini Reviewer 只能閱讀規則。它可以指出授權條件過寬,卻不知道攻擊是否能在目前環境重現。

今天的測試補上兩項證據:

  1. 未登入請求確實遭到拒絕。
  2. 另一位登入者確實能跨帳號讀寫資料。

未來的 Agent 可以先分析程式碼,再呼叫測試工具驗證 Finding。報告也能附上實際指令與結果。

理想的 Finding 應包含:

問題:任何登入者都能存取其他使用者的專案
證據:allow read, write: if request.auth != null
前提:攻擊者具備一般登入帳號
驗證:npm run authz:demo
結果:跨帳號讀取與更新皆為 ALLOWED
影響:未授權資料讀取、竄改或刪除
修正:比較 request.auth.uid 與文件 ownerId

這種報告同時包含靜態證據與動態驗證。開發者不必只相信模型的判斷。

今天的結論

Authentication 回答「你是誰」,Authorization 回答「你能做什麼」。

request.auth != null 只能擋住未登入訪客。系統如果沒有繼續檢查資源擁有者或角色,任何登入者仍可能跨帳號操作資料。

今天也替 Production Readiness Agent 加入一項重要原則:

Agent 找到疑似漏洞後,應盡可能建立可重現測試。測試結果才能證明問題是否成立。

明天,我們會檢查 API、錯誤訊息與 Log,確認個人資料會不會在正常功能以外的地方外洩。

參考資料


上一篇
Day 6|第一次交給 Gemini:用 Google AI Studio 建立程式碼 Reviewer
下一篇
Day 8|API、錯誤訊息與 Log:個人資料是如何不小心外洩的?
系列文
從 Vibe Coding 到 Production:用 Google AI 打造上線守門員10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言